package src.main.java.com.example.image.skew.correction.demo;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.RotatedRect;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.util.ArrayList;
import java.util.List;

/**
 * < >类功能说明</ >
 *
 * @version V1.0
 * @date 2025/1/1 15:04
 */
public class Main {
    public static void main(String[] args) {
        //初始化OpenCV
        //OpenCVConfig.initOpenCV();
        OpenCVUtils openCV = OpenCVUtils.getInstance();
        openCV.printVersion();

        // 读取图像
        String inputImagePath = "src/main/resources/input/test.jpg";
        Mat src = Imgcodecs.imread(inputImagePath, Imgcodecs.IMREAD_GRAYSCALE);

        if (src.empty()) {
            System.out.println("无法加载图像，请检查路径是否正确！");
            return;
        }

        // 图像预处理
        Mat binary = new Mat();
        Imgproc.threshold(src, binary, 0, 255, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_OTSU);

        // 查找轮廓
        List<MatOfPoint> contours = new ArrayList<>();
        Mat hierarchy = new Mat();
        Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

        // 方法 1：使用最小外接矩形计算角度
        double angleFromRect = getSkewAngleFromRect(binary);
        System.out.println("最小外接矩形计算的角度: " + angleFromRect);

        // 方法 2：使用霍夫变换计算角度
        double angleFromHough = getSkewAngleFromHough(binary);
        System.out.println("霍夫变换计算的角度: " + angleFromHough);

        // 综合两种方法的结果
        double finalAngle = (angleFromRect + angleFromHough) / 2.0;
        System.out.println("最终使用的角度: " + finalAngle);

        // 校正图像
        Mat corrected = new Mat();
        Point center = new Point(src.cols() / 2.0, src.rows() / 2.0);
        Mat rotationMatrix = Imgproc.getRotationMatrix2D(center, -finalAngle, 1.0);
        Imgproc.warpAffine(src, corrected, rotationMatrix, src.size(), Imgproc.INTER_LINEAR, Core.BORDER_REPLICATE);

        // 保存校正后的图像
        String outputImagePath = "src/main/resources/output/corrected_image.jpg";
        Imgcodecs.imwrite(outputImagePath, corrected);

        System.out.println("图像纠斜完成，已保存到: " + outputImagePath);

    }

    /**
     * 最小外接矩形算法计算倾斜角度
     */
    private static double getSkewAngleFromRect(Mat binary) {
        List<MatOfPoint> contours =  new ArrayList<>();
        Mat hierarchy= new Mat();
        Imgproc.findContours(binary,contours,hierarchy,Imgproc.RETR_EXTERNAL,Imgproc.CHAIN_APPROX_SIMPLE);

        List<Double> angles = new ArrayList<>();
        double minArea = 1000;//过滤小面积轮廓

        for (MatOfPoint contour: contours) {
            double area = Imgproc.contourArea(contour);
            if (area > minArea) {
                RotatedRect rect = Imgproc.minAreaRect(new MatOfPoint2f(contour.toArray()));
                double angle = rect.angle;

                //调整角度范围
                if(rect.size.width < rect.size.height){
                    angle += 90;
                }
                if(angle>45){
                    angle -= 90;
                }
                angles.add(angle);
            }
        }
        //计算平均角度
        double sum = 0;
        for (double angle : angles) {
            sum += angle;
        }
        return angles.isEmpty()?0:sum / angles.size();
    }

    /**
     * 使用霍夫变换计算倾斜角度
     */
    private static double getSkewAngleFromHough(Mat binary) {
        Mat lines = new Mat();
        Imgproc.HoughLinesP(binary, lines, 1, Math.PI / 180, 100, 100, 10);

        List<Double> angles = new ArrayList<>();
        for (int i = 0; i < lines.rows(); i++) {
            double[] line = lines.get(i,0);
            double x1=line[0],y1=line[1],x2=line[2],y2=line[3];
            double angle = Math.toDegrees(Math.atan2(y2-y1,x2-x1));
            if (Math.abs(angle)<45){
                angles.add(angle);
            }
        }

        //计算平均角度
        double sum =0;
        for (double angle:angles){
            sum += angle;
        }
        return angles.isEmpty()?0:sum/angles.size();
    }
}
